﻿using System.Collections.Generic;
using System.IO;
using Common;
using UI.Panels;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;

namespace UI
{
    public class UIManager : Singleton<UIManager>
    {
        private const string UIPrefabRootPath = "Prefabs/UI/Prefab/";
        private const string UIPathBin = "GameConfig/UIPath.bin";
        
        
        private Transform m_UIRoot;
        private readonly List<UILayer> m_UILayers = new (4);
        
        private readonly Dictionary<string, string> m_UIPrefabPath = new();

        private readonly Dictionary<string, GameObject> m_CachedUiObject = new();
        
        public void Init()
        {
            InitUIRoot();
            LoadUIPrefabPath();
        }

        private void InitUIRoot()
        {
            if (m_UIRoot != null)
            {
                return;
            }
            
            var prefab = ResourceLoader.Load<GameObject>("Prefabs/UI/UIRoot");
            var go = GameObject.Instantiate(prefab);
            go.name = "UIRoot";
            GameObject.DontDestroyOnLoad(go);
            m_UIRoot = go.transform;
            
            var uiLayerTemplate = go.transform.Find("UILayerTemplate");
            uiLayerTemplate.gameObject.SetActive(false);

            for (int i = 0; i < 4; ++i)
            {
                var uiLayerRoot = GameObject.Instantiate(uiLayerTemplate.gameObject, m_UIRoot);
                uiLayerRoot.name = "UILayer_" + i;
                uiLayerRoot.SetActive(true);
                
                m_UILayers.Add(new UILayer(uiLayerRoot.transform, i));
            }
        }

        /// <summary>
        /// 从bin文件加载每个预制所在的路径。 key: 预制名 value: 路径
        /// </summary>
        private void LoadUIPrefabPath()
        {
            if (m_UIPrefabPath.Count > 0)
            {
                return;
            }
            
            if (!File.Exists(Path.Combine(Application.dataPath, UIPathBin)))
            {
#if UNITY_EDITOR
                Debug.LogError("UIPath.bin不存在");
#endif
                return;
            }

            using var fs = new FileStream(Path.Combine(Application.dataPath, UIPathBin), FileMode.Open);
            using var br = new BinaryReader(fs);
            while (br.BaseStream.Position != br.BaseStream.Length)
            {
                string fileName = br.ReadString();
                string path = br.ReadString();
                m_UIPrefabPath.Add(fileName, path);
            }
        }


        #region UILayer

        public Transform GetUICanvasRoot(int uiLayerIndex)
        {
            if (uiLayerIndex < 0 || uiLayerIndex >= m_UILayers.Count)
            {
                return null;
            }
            return m_UILayers[uiLayerIndex].canvasRoot;
        }
        
        public UILayer GetUILayer(int uiLayerIndex)
        {
            if (uiLayerIndex < 0 || uiLayerIndex >= m_UILayers.Count)
            {
                return null;
            }
            return m_UILayers[uiLayerIndex];
        }
        
        public void SwitchCullingMaskEnable(int uiLayerIndex, bool enable)
        {
            if (uiLayerIndex < 0 || uiLayerIndex >= m_UILayers.Count)
            {
                return;
            }
            m_UILayers[uiLayerIndex]?.SwitchCullingMaskEnable(enable);
        }
        

        #endregion


        #region UIPanel

        private UIPanel CreatePanel(string panelName, GameObject prefab, int uiLayerIndex, int order)
        {
            var parent = GetUICanvasRoot(uiLayerIndex);
            if (parent == null)
            {
#if UNITY_EDITOR
                EditorUtility.DisplayDialog("Error", $"GetUICanvasRoot({uiLayerIndex})失败", "OK");
#endif
                return null;
            }
            
            var go = GameObject.Instantiate(prefab, parent);
            m_CachedUiObject.Add(panelName, go);
            go.name = panelName;
            var canvas = go.AddComponent<Canvas>();
            canvas.overrideSorting = true;
            canvas.sortingOrder = order;
            go.AddComponent<GraphicRaycaster>();
            
            var uiPanel = go.GetComponent<UIPanel>();
            if (uiPanel != null)
            {
                uiPanel.uiLayerIndex = uiLayerIndex;
                uiPanel.orderInLayer = order;
            }
            
            return uiPanel;
        }

        public void SetPanelLayer(UIPanel uiPanel, int uiLayerIndex, int order)
        {
            if (uiPanel == null) return;

            if (uiPanel.uiLayerIndex != uiLayerIndex)
            {
                var uiLayer = m_UILayers[uiLayerIndex];
                if (uiLayer != null)
                {
                    uiLayer.AddPanel(uiPanel.gameObject);
                }
            }
            

            if (uiPanel.orderInLayer != order)
            {
                var canvas = uiPanel.GetComponent<Canvas>();
                if (canvas != null)
                {
                    canvas.overrideSorting = true;
                    canvas.sortingOrder = order;
                }
            }

        }

        private string GetPanelPrefabPath(string panelName)
        {
            if (!m_UIPrefabPath.TryGetValue(panelName, out var path))
            {
#if UNITY_EDITOR
                EditorUtility.DisplayDialog("Error", $"UIPrefabPath中不存在{panelName}", "OK");
#endif
                return null;
            }

            return UIPrefabRootPath + path;
        }

        public GameObject FindOpenedUIObject(string panelName)
        {
            if (m_CachedUiObject.TryGetValue(panelName, out var uiPanel))
            {
                return uiPanel;
            }

            return null;
        }

        private UIPanel OpenPanelIfInCache(string panelName, int uiLayerIndex, int order)
        {
            var uiPanel = FindOpenedUIObject(panelName)?.GetComponent<UIPanel>();
            if (uiPanel == null)
            {
                return null;
            }
            
            SetPanelLayer(uiPanel, uiLayerIndex, order);
            uiPanel.gameObject.SetActive(true);
            return uiPanel;
        }

        public UIPanel OpenPanel(string panelName, int uiLayerIndex, int order)
        {
            var uiPanel = OpenPanelIfInCache(panelName, uiLayerIndex, order);
            if (uiPanel != null)
            {
                return uiPanel;
            }
            
            var path = GetPanelPrefabPath(panelName);
            if (string.IsNullOrEmpty(path))
            {
                return null;
            }

            var prefab = ResourceLoader.Load<GameObject>(path);
            if (prefab == null)
            {
#if UNITY_EDITOR
                EditorUtility.DisplayDialog("Error", $"加载{path}失败", "OK");
#endif
                return null;
            }
            
            return CreatePanel(panelName, prefab, uiLayerIndex, order);
        }

        public void OpenPanelAsync(string panelName, int uiLayerIndex, int order)
        {
            var uiPanel = OpenPanelIfInCache(panelName, uiLayerIndex, order);
            if (uiPanel != null)
            {
                return;
            }
            
            var path = GetPanelPrefabPath(panelName);
            if (string.IsNullOrEmpty(path))
            {
                return;
            }
            
            ResourceLoader.LoadAsync<GameObject>(path, (ResourceRequest request) =>
            {
                if (request == null || request.asset == null)
                {
#if UNITY_EDITOR
                    EditorUtility.DisplayDialog("Error", $"加载{path}失败", "OK");
#endif
                    return;
                }
                CreatePanel(panelName, request.asset as GameObject, uiLayerIndex, order);
                
            });
        }
        
        public void ClosePanel(string panelName)
        {
            if (!m_CachedUiObject.TryGetValue(panelName, out var go))
            {
                return;
            }

            m_CachedUiObject.Remove(panelName);
            GameObject.Destroy(go);
        }

        public void ClosePanel(UIPanel panel)
        {
            m_CachedUiObject.Remove(panel.name, out var go);

            Object.Destroy(panel.gameObject);
        }

        #endregion
        
    }
}